
format PE64 GUI 5.0
entry start

include 'win64a.inc'
include 'ddraw64.inc'

section '.text' code readable executable

  start:

        sub     rsp,8

        and     [DDraw],0
        and     [DDSPrimary],0
        and     [DDSBack],0

        invoke  GetModuleHandle,NULL
        mov     [hinstance],rax
        mov     [wc.hInstance],rax

        invoke  LoadIcon,NULL,IDI_APPLICATION
        mov     [wc.hIcon],rax
        invoke  LoadCursor,NULL,IDC_ARROW
        mov     [wc.hCursor],rax
        invoke  RegisterClassEx,wc
        test    rax,rax
        jz      startup_error

        invoke  CreateWindowEx,\
                0,_class,_title,WS_POPUP+WS_VISIBLE,0,0,0,0,NULL,NULL,[hinstance],NULL
        test    rax,rax
        jz      startup_error
        mov     [hwnd],rax

        invoke  DirectDrawCreate,NULL,DDraw,NULL
        test    rax,rax
        jnz     ddraw_error

        cominvk DDraw,SetCooperativeLevel,\
                [hwnd],DDSCL_EXCLUSIVE+DDSCL_FULLSCREEN
        test    rax,rax
        jnz     ddraw_error

        cominvk DDraw,SetDisplayMode,\
                640,480,32
        test    rax,rax
        jnz     ddraw_error

        mov     [ddsd.dwSize],sizeof.DDSURFACEDESC
        mov     [ddsd.dwFlags],DDSD_CAPS+DDSD_BACKBUFFERCOUNT
        mov     [ddsd.ddsCaps.dwCaps],DDSCAPS_PRIMARYSURFACE+DDSCAPS_FLIP+DDSCAPS_COMPLEX
        mov     [ddsd.dwBackBufferCount],1
        cominvk DDraw,CreateSurface,\
                ddsd,DDSPrimary,NULL
        or      eax,eax
        jnz     ddraw_error

        mov     [ddscaps.dwCaps],DDSCAPS_BACKBUFFER
        cominvk DDSPrimary,GetAttachedSurface,\
                ddscaps,DDSBack
        or      eax,eax
        jnz     ddraw_error

refresh:

        cominvk DDSPrimary,IsLost
        test    rax,rax
        jz      paint
        cmp     eax,DDERR_SURFACELOST
        jne     end_loop
        cominvk DDSPrimary,Restore

    paint:
        mov     [ddsd.dwSize],sizeof.DDSURFACEDESC
        mov     [ddsd.dwFlags],0
        cominvk DDSBack,Lock,NULL,ddsd,DDLOCK_SURFACEMEMORYPTR+DDLOCK_WAIT,NULL
        test    rax,rax
        jnz     main_loop
        mov     rdi,[ddsd.lpSurface]
        mov     r10d,[ddsd.lPitch]
        xor     edx,edx
        movsd   xmm8,[y_top]
    screen:
        xor     ebx,ebx
        movsd   xmm7,[x_left]
        unpcklpd xmm7,xmm8
    row:
        mov     rcx,255
        xorpd   xmm1,xmm1
    iterate:

        movapd  xmm3,xmm1
        unpckhpd xmm3,xmm3
        mulsd   xmm3,xmm1
        addsd   xmm3,xmm3

        mulpd   xmm1,xmm1
        movapd  xmm2,xmm1               ; for SSE3-capable processor
        unpckhpd xmm2,xmm2              ; these three instructions can be
        subsd   xmm1,xmm2               ; replaced with HSUBPD XMM1,XMM1
        unpcklpd xmm1,xmm3
        addpd   xmm1,xmm7

        movapd  xmm0,xmm1
        mulpd   xmm0,xmm0
        movapd  xmm2,xmm0               ; for SSE3-capable processor
        shufpd  xmm2,xmm2,1             ; these three instructions can be
        addsd   xmm0,xmm2               ; replaced with HADDPD XMM0,XMM0
        sqrtpd  xmm0,xmm0
        comisd  xmm0,[limit]
        ja      over

        loop    iterate
    over:
        xor     al,al
        stosb
        mov     al,cl
        stosb
        ror     al,3
        stosb
        stosb

        movsd   xmm0,[x_step]
        addpd   xmm7,xmm0
        inc     ebx
        cmp     ebx,640
        jb      row
        sub     rdi,640*4
        add     rdi,r10
        subsd   xmm8,[y_step]
        inc     edx
        cmp     edx,480
        jb      screen

        mov     [refresh_needed],0
        cominvk DDSBack,Unlock,NULL
        cominvk DDSPrimary,Flip,0,0

main_loop:

        invoke  PeekMessage,msg,NULL,0,0,PM_NOREMOVE
        or      eax,eax
        jz      no_message
        invoke  GetMessage,msg,NULL,0,0
        cmp     eax,1
        jb      end_loop
        jne     no_message
        invoke  TranslateMessage,msg
        invoke  DispatchMessage,msg

        cmp     [refresh_needed],0
        jne     refresh

        jmp     main_loop

    no_message:
        invoke  WaitMessage
        jmp     main_loop

ddraw_error:
        invoke  wsprintf,buffer,_ddraw_error,rax
        invoke  MessageBox,[hwnd],buffer,_error,MB_OK+MB_ICONERROR
        invoke  DestroyWindow,[hwnd]
        invoke  PostQuitMessage,2
        jmp     main_loop

startup_error:
        invoke  MessageBox,[hwnd],_startup_error,_error,MB_OK+MB_ICONERROR
        invoke  ExitProcess,1

end_loop:
        cominvk DDraw,RestoreDisplayMode

        cmp     [DDSBack],0
        je      back_surface_released
        cominvk DDSPrimary,DeleteAttachedSurface,0,DDSBack
    back_surface_released:
        cmp     [DDSPrimary],0
        je      primary_surface_released
        cominvk DDSPrimary,Release
    primary_surface_released:
        cmp     [DDraw],0
        je      ddraw_released
        cominvk DDraw,Release
    ddraw_released:

        invoke  ExitProcess,[msg.wParam]

proc WindowProc uses rbx rsi rdi, hwnd,wmsg,wparam,lparam
        cmp     edx,WM_CREATE
        je      .wmcreate
        cmp     edx,WM_DESTROY
        je      .wmdestroy
        cmp     edx,WM_LBUTTONDOWN
        je      .wmlbuttondown
        cmp     edx,WM_RBUTTONDOWN
        je      .wmrbuttondown
        cmp     edx,WM_KEYDOWN
        je      .wmkeydown
        cmp     edx,WM_ACTIVATE
        je      .wmactivate
    .defwindowproc:
        invoke  DefWindowProc,rcx,rdx,r8,r9
        jmp     .finish
    .wmcreate:
        xor     eax,eax
        jmp     .finish
    .wmactivate:
        test    r8,r8
        jz      .finish
        or      [refresh_needed],1
        jmp     .finish
    .wmlbuttondown:
        movapd  xmm0,[step]
        divpd   xmm0,[zoom]
        movapd  xmm1,xmm0
        subpd   xmm1,[step]
        movapd  [step],xmm0
        movzx   eax,r9w
        cvtsi2sd xmm3,eax
        shr     r9,16
        movzx   eax,r9w
        cvtsi2sd xmm4,eax
        unpcklpd xmm3,xmm4
        mulpd   xmm1,xmm3
        xorpd   xmm1,[negate]
        addpd   xmm1,[origin]
        movapd  [origin],xmm1
        or      [refresh_needed],1
        jmp     .finish
    .wmrbuttondown:
        movapd  xmm0,[step]
        mulpd   xmm0,[zoom]
        movapd  xmm1,xmm0
        subpd   xmm1,[step]
        movapd  [step],xmm0
        movzx   eax,r9w
        cvtsi2sd xmm3,eax
        shr     r9,16
        movzx   eax,r9w
        cvtsi2sd xmm4,eax
        unpcklpd xmm3,xmm4
        mulpd   xmm1,xmm3
        xorpd   xmm1,[negate]
        addpd   xmm1,[origin]
        movapd  [origin],xmm1
        or      [refresh_needed],1
        jmp     .finish
    .wmkeydown:
        cmp     r8d,VK_ESCAPE
        jne     .finish
    .wmdestroy:
        invoke  PostQuitMessage,0
        xor     eax,eax
    .finish:
        ret
endp

section '.data' data readable writeable

  wc WNDCLASSEX sizeof.WNDCLASSEX,0,WindowProc,0,0,NULL,NULL,NULL,NULL,NULL,_class,NULL

  _title db 'flat assembler DirectDraw application',0
  _class db 'FDDRAW64',0

  _error db 'Error',0
  _startup_error db 'Startup failed',0
  _ddraw_error db 'Direct Draw initialization failed (error code 0x%x).',0

  align 16                              ; SSE data follows

  label origin dqword
   x_left dq -2.2
   y_top dq 1.25

  label step dqword
   x_step dq 0.0045
   y_step dq 0.0052

  label zoom dqword
   dq 1.2,1.2

  label negate dqword
   dq 8000000000000000h,0

  limit dq 2.5

section '.bss' readable writeable

  hinstance dq ?
  hwnd dq ?
  msg MSG

  ddsd DDSURFACEDESC
  ddscaps DDSCAPS

  DDraw DirectDraw
  DDSPrimary DirectDrawSurface
  DDSBack DirectDrawSurface

  rect RECT

  refresh_needed dd ?

  buffer rb 100h


section '.idata' import data readable

  library kernel32,'KERNEL32.DLL',\
          user32,'USER32.DLL',\
          ddraw,'DDRAW.DLL'

  include 'api\kernel32.inc'
  include 'api\user32.inc'

  import ddraw,\
         DirectDrawCreate,'DirectDrawCreate'
